using System;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Unity.Extensions;
using Xunit;

namespace Unity.Extensions
{
    public class RemotingInterceptionReflectorTest
    {
        [Fact]
        public void CanAddHandlersInInheritedClass()
        {
            PolicyList policies = new PolicyList();
            MethodBase method = typeof(DerivedWithAddedIntercepts).GetMethod("InterceptedMethod");

            InterceptionReflector.Reflect<DerivedWithAddedIntercepts>(policies, new StubObjectFactory());
            IRemotingInterceptionPolicy policy = policies.Get<IRemotingInterceptionPolicy>(new NamedTypeBuildKey(typeof(DerivedWithAddedIntercepts)));

            Assert.Equal(1, policy.Count);
            Assert.Equal(2, policy[method].Count);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method][0]);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method][1]);
        }

        [Fact]
        public void InterceptionIsInherited()
        {
            PolicyList policies = new PolicyList();
            MethodBase method = typeof(DerivedOneMethod).GetMethod("InterceptedMethod");

            InterceptionReflector.Reflect<DerivedOneMethod>(policies, new StubObjectFactory());
            IRemotingInterceptionPolicy policy = policies.Get<IRemotingInterceptionPolicy>(new NamedTypeBuildKey(typeof(DerivedOneMethod)));

            Assert.Equal(1, policy.Count);
            Assert.Equal(1, policy[method].Count);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method][0]);
        }

        [Fact]
        public void NonMBROTypeIncompatibleWithRemoting()
        {
            PolicyList policies = new PolicyList();

            Assert.Throws<InvalidOperationException>(
                () => InterceptionReflector.Reflect<NonMBRO>(policies, new StubObjectFactory()));
        }

        [Fact]
        public void OneInterceptedMethod()
        {
            PolicyList policies = new PolicyList();
            MethodBase method = typeof(OneMethod).GetMethod("InterceptedMethod");

            InterceptionReflector.Reflect<OneMethod>(policies, new StubObjectFactory());
            IRemotingInterceptionPolicy policy = policies.Get<IRemotingInterceptionPolicy>(new NamedTypeBuildKey(typeof(OneMethod)));

            Assert.NotNull(policy);
            Assert.Equal(1, policy.Count);
            Assert.Equal(1, policy[method].Count);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method][0]);
        }

        [Fact]
        public void TwoAttributesOnOneMethod()
        {
            PolicyList policies = new PolicyList();
            MethodBase method = typeof(OneMethodTwoAttributes).GetMethod("InterceptedMethod");

            InterceptionReflector.Reflect<OneMethodTwoAttributes>(policies, new StubObjectFactory());
            IRemotingInterceptionPolicy policy = policies.Get<IRemotingInterceptionPolicy>(new NamedTypeBuildKey(typeof(OneMethodTwoAttributes)));

            Assert.Equal(1, policy.Count);
            Assert.Equal(2, policy[method].Count);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method][0]);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method][1]);
        }

        [Fact]
        public void TwoInterceptedMethods()
        {
            PolicyList policies = new PolicyList();
            MethodBase method1 = typeof(TwoMethods).GetMethod("InterceptedMethod1");
            MethodBase method2 = typeof(TwoMethods).GetMethod("InterceptedMethod2");

            InterceptionReflector.Reflect<TwoMethods>(policies, new StubObjectFactory());
            IRemotingInterceptionPolicy policy = policies.Get<IRemotingInterceptionPolicy>(new NamedTypeBuildKey(typeof(TwoMethods)));

            Assert.Equal(2, policy.Count);
            Assert.Equal(1, policy[method1].Count);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method1][0]);
            Assert.Equal(1, policy[method2].Count);
            Assert.IsAssignableFrom(typeof(RecordingHandler), policy[method2][0]);
        }

        internal class DerivedOneMethod : OneMethod {}

        internal class DerivedWithAddedIntercepts : OneMethod
        {
            [RemotingIntercept(typeof(RecordingHandler))]
            public override void InterceptedMethod() {}
        }

        internal class NonMBRO
        {
            [RemotingIntercept(typeof(RecordingHandler))]
            public void InterceptedMethod() {}
        }

        internal class OneMethod : MarshalByRefObject
        {
            [RemotingIntercept(typeof(RecordingHandler))]
            public virtual void InterceptedMethod() {}

            public void NonInterceptedMethod() {}
        }

        internal class OneMethodTwoAttributes : MarshalByRefObject
        {
            [RemotingIntercept(typeof(RecordingHandler))]
            [RemotingIntercept(typeof(RecordingHandler))]
            public void InterceptedMethod() {}
        }

        internal class TwoMethods : MarshalByRefObject
        {
            [RemotingIntercept(typeof(RecordingHandler))]
            public void InterceptedMethod1() {}

            [RemotingIntercept(typeof(RecordingHandler))]
            public void InterceptedMethod2() {}
        }
    }
}